package com.glodon.paas.document.storage.web;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.HandlerMapping;

import com.glodon.paas.document.storage.Storage;
import com.glodon.paas.document.storage.StorageFile;
import com.glodon.paas.document.storage.util.OssUtil;
import com.glodon.paas.document.storage.web.oss.Bucket;
import com.glodon.paas.document.storage.web.oss.CompleteMultipartUploadResult;
import com.glodon.paas.document.storage.web.oss.InitiateMultipartUploadResult;
import com.glodon.paas.document.storage.web.oss.ListAllMyBucketsResult;
import com.glodon.paas.document.storage.web.oss.Owner;

@Controller
@RequestMapping("/oss")
public class OSSController {
    @Autowired
    private Storage storage;
    private String separator = "/";

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public void getBucket(HttpServletResponse response) throws IOException {
        ListAllMyBucketsResult listAllMyBucketsResult = new ListAllMyBucketsResult();
        Owner owner = new Owner();
        owner.setId("1633541454828561");
        owner.setDisplayName("1633541454828561");
        listAllMyBucketsResult.setOwner(owner);
        List<Bucket> buckets = new ArrayList<Bucket>();
        Bucket bucket = new Bucket();
        bucket.setName("glodon_paas");
        bucket.setCreationDate("2012-08-01T03:24:00.000Z");
        buckets.add(bucket);
        listAllMyBucketsResult.setBuckets(buckets);
        OssUtil.toXml(listAllMyBucketsResult, response.getOutputStream());
    }

    @RequestMapping(value = "/{bucket}/**", method = RequestMethod.HEAD)
    public void headObject(@PathVariable String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String path = getBucketPath(bucket, request);
        StorageFile file = storage.getFile(path);
        if (file == null) {
            response.sendError(404);
        } else {
            //            response.setHeader("Content-Length", "100");
            InputStream in = storage.get(path);
            ServletOutputStream out = response.getOutputStream();
            IOUtils.copy(in, out);
            in.close();
        }
    }

    @RequestMapping(value = "/{bucket}/**", method = RequestMethod.GET)
    public void getObject(@PathVariable String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String path = getBucketPath(bucket, request);
        StorageFile storageFile = storage.getFile(path);
        String range = request.getHeader("Range");
        long start = 0;
        long end = storageFile.length();
        if (range != null) {
            String startStr = StringUtils.substringBetween(range, "bytes=", "-");
            String endStr = StringUtils.substringAfter(range, "-");
            if (StringUtils.isNumeric(startStr) && StringUtils.isNumeric(endStr)) {
                start = Long.parseLong(startStr);
                if (StringUtils.isNotBlank(endStr)) {
                    end = Long.parseLong(endStr);
                    if (end != storageFile.length() - 1)
                        throw new IOException("bad request length is invalid, file length: " + storageFile.length()
                                + ", request length: " + end);
                }
            }
        }
        response.setHeader("Content-Length", String.valueOf(storageFile.length() - start));
        InputStream in = storageFile.getInputStream(start);
        ServletOutputStream out = response.getOutputStream();
        IOUtils.copyLarge(in, out);
        in.close();
        out.close();
    }

    private String getBucketPath(String bucket, HttpServletRequest request) {
        String path = getPath(request);
        return separator + bucket + separator + path;
    }

    private String getPath(HttpServletRequest request) {
        return (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    }

    @RequestMapping(value = "/{bucket}/**", method = RequestMethod.PUT)
    public void putObject(@PathVariable String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String uploadId = request.getParameter("uploadId");
        if (uploadId == null) {
            upload(bucket, request);
        } else {
            int partNumber = Integer.parseInt(request.getParameter("partNumber"));
            uploadPart(bucket, request, uploadId, partNumber, response);
        }
    }

    private void uploadPart(String bucket, HttpServletRequest request, String uploadId, int partNumber,
            HttpServletResponse response) throws IOException {
        String path = getBucketPath(bucket, request);
        long length = Long.parseLong(request.getHeader("Content-Length"));
        ServletInputStream is = request.getInputStream();
        String id = storage.append(path, uploadId, partNumber, is, length);
        is.close();
        response.setHeader("ETag", id);
    }

    private void upload(String bucket, HttpServletRequest request) throws IOException {
        String path = getBucketPath(bucket, request);
        ServletInputStream is = request.getInputStream();
        storage.save(path, is);
        is.close();
    }

    @RequestMapping(value = "/{bucket}/**", method = RequestMethod.POST)
    public void postObject(@PathVariable String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        if (request.getParameter("uploads") != null) {
            initMultipartUpload(bucket, request, response);
        } else if (request.getParameter("uploadId") != null) {
            completeMultipartUpload(bucket, request, response);
        }
    }

    private void completeMultipartUpload(String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        String path = getBucketPath(bucket, request);
        response.setContentType("application/xml");
        String id = request.getParameter("uploadId");
        String objectId = storage.completePart(path, id);
        CompleteMultipartUploadResult completeMultipartUploadResult = new CompleteMultipartUploadResult();
        completeMultipartUploadResult.setBucket(bucket);
        completeMultipartUploadResult.setKey(getPath(request));
        completeMultipartUploadResult.seteTag(objectId);
        OssUtil.toXml(completeMultipartUploadResult, response.getOutputStream());
    }

    private void initMultipartUpload(String bucket, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        response.setContentType("application/xml");
        String path = getBucketPath(bucket, request);
        String id = storage.initPart(path);
        InitiateMultipartUploadResult initiateMultipartUploadResult = new InitiateMultipartUploadResult();
        initiateMultipartUploadResult.setBucket(bucket);
        initiateMultipartUploadResult.setKey(getPath(request));
        initiateMultipartUploadResult.setUploadId(id);
        OssUtil.toXml(initiateMultipartUploadResult, response.getOutputStream());
    }
}
